#include "Nucleo.h"

/* ---- Inicio dos metodos publicos ---- */

Nucleo::Nucleo()
{
        this->mClock = 0;
        this->mTempoOcioso = 0;
}

Nucleo::~Nucleo()
{
	
}

void
Nucleo::executa()
{
	char
	naoservepranada;
	int i;
    do{
    	this->imprimeSituacao();
        std::cout << "Pressione alguma letra e depois ENTER para continuar" << std::endl;
        std::cin >> naoservepranada;
    	this->verificaDispositivos();
    	if(!mPronto.empty())
    	{
        	this->escalonador();
        	i = this->executaProcesso( this->mTempo );
    	}
    	else
    	{
    		i = 1;
    	}
        this->mClock += i; /* vai receber o tempo do retorno do metodo acima */
	}while(!mPronto.empty() || !mBloqueadoUsando.empty());
}

int
Nucleo::getClock()
{
	return this->mClock;
}

int
Nucleo::getOcioso()
{
	return this->mTempoOcioso;
}

void
Nucleo::insereListaPronto( BCP aInserir )
{
	this->mClock++;
	this->mTempoOcioso++;
	this->mPronto.push_back(aInserir);
}

void
Nucleo::insereDispositivo( char vNome, int vNumeroDisponiveis )
{
	Dispositivo
	novo;
	
	novo.setNome(vNome);
	novo.setNumeroDisponiveis(vNumeroDisponiveis);
	
	this->mDispositivos.push_back(novo);
}

/* ---- Fim dos metodos publicos ---- */

/* ---- Inicio dos metodos privados ---- */

void
Nucleo::insereListaPronto()
{
	this->mClock++;
	this->mTempoOcioso++;
	this->mPronto.push_back(this->mExecucao);
}

void
Nucleo::insereListaBloqueadoEsperando()
{
	this->mClock++;
	this->mTempoOcioso++;
	this->mBloqueadoEsperando.push_back(this->mExecucao);
}

void
Nucleo::insereListaBloqueadoUsando()
{
	this->mClock++;
	this->mTempoOcioso++;
	this->mBloqueadoUsando.push_back(this->mExecucao);
}

int
Nucleo::executaProcesso(int vTempoDisponivel)
{
	
	int
	tempoDisponivelInicial = vTempoDisponivel;
	
	int
	tempoGasto;
	
	char
	tipoTarefa;
	
	int
	tempoTarefa;
	
	tarefa
	tarefaAuxiliar;
	
	bool
	sai = false;
	
	while( vTempoDisponivel > 0 && sai == false )
	{
		this->mExecucao.getTarefaAtual(&tipoTarefa, &tempoTarefa);
		tarefaAuxiliar.tipo = tipoTarefa;
		tarefaAuxiliar.tempoQFalta = tempoTarefa;
		if( ( this->mExecucao.getUsandoDispositivo( ) == tarefaAuxiliar.tipo ) || this->dispositivoEstaDisponivel( tarefaAuxiliar.tipo ) ) /*  esta usando ou esta disponivel */
		{
			if( this->mExecucao.getUsandoDispositivo( ) != tarefaAuxiliar.tipo )
            {
            	this->mExecucao.setUsandoDispositivo( tarefaAuxiliar.tipo );
                this->reservaDispositivo( tarefaAuxiliar.tipo );
            }
            if( vTempoDisponivel > tarefaAuxiliar.tempoQFalta )
            {
            	vTempoDisponivel -= tarefaAuxiliar.tempoQFalta;
                this->mExecucao.setTarefaAtual( 0 );
                this->liberaDispositivo( tarefaAuxiliar.tipo );
                this->mExecucao.setUsandoDispositivo( '0' );
                this->mExecucao.proximaTarefa();
                if( this->mExecucao.fimDoProcesso() )
                {
                	tempoGasto = tempoDisponivelInicial - vTempoDisponivel;
                	vTempoDisponivel = 0;
                }
			}
            else if( vTempoDisponivel == tarefaAuxiliar.tempoQFalta )
            {
            	vTempoDisponivel = 0;
                this->mExecucao.setTarefaAtual( 0 );
                this->liberaDispositivo( tarefaAuxiliar.tipo );
                this->mExecucao.setUsandoDispositivo( '0' );
                this->mExecucao.proximaTarefa();
                tempoGasto = tempoDisponivelInicial;
                if( !this->mExecucao.fimDoProcesso() )
           		{
             		this->insereListaPronto();
            	}
			}
            else
            {
            	tarefaAuxiliar.tempoQFalta -= vTempoDisponivel;
                this->mExecucao.setTarefaAtual( tarefaAuxiliar.tempoQFalta );
                if( tarefaAuxiliar.tipo != '0' )
                {
                	this->mExecucao.setClockQueSai( tarefaAuxiliar.tempoQFalta + this->mClock + ( tempoDisponivelInicial - vTempoDisponivel ) );
                	this->insereListaBloqueadoUsando();
                }
                else
                {
                	this->insereListaPronto();
                }
                vTempoDisponivel = 0;
                tempoGasto = tempoDisponivelInicial;
            }
		}
		else
        {
        	this->insereListaBloqueadoEsperando();
            sai = true;
            tempoGasto = 1;
        }
	}
	return tempoGasto;
}

bool
Nucleo::dispositivoEstaDisponivel( char vNomeDispositivo )
{
	if( vNomeDispositivo == '0' )
	{
		this->mClock++;
		this->mTempoOcioso++;
		return true;
	}
	this->mIteradorDispositivos = this->mDispositivos.begin();
	while( this->mIteradorDispositivos != this->mDispositivos.end() && (*mIteradorDispositivos).getNome() != vNomeDispositivo )
	{
		this->mClock++;
		this->mTempoOcioso++;
		this->mIteradorDispositivos++;
	}
	if( (*mIteradorDispositivos).getNome() == vNomeDispositivo )
	{	
		if( (*mIteradorDispositivos).getNumeroDisponiveis() > 0 )
		{
			return true;
		}
		else
		{
			return false;
		}
	}
	return false;
}

void
Nucleo::reservaDispositivo( char vNomeDispositivo )
{
	if( vNomeDispositivo == '0' )
	{
		this->mClock++;
		this->mTempoOcioso++;
		return;
	}
	int
	i;
	this->mIteradorDispositivos = this->mDispositivos.begin();
	while( this->mIteradorDispositivos != this->mDispositivos.end() && (*mIteradorDispositivos).getNome() != vNomeDispositivo )
	{
		this->mClock++;
		this->mTempoOcioso++;
		this->mIteradorDispositivos++;
	}
	if( (*mIteradorDispositivos).getNome() == vNomeDispositivo )
	{
		i = (*mIteradorDispositivos).getNumeroDisponiveis();
		i--;
		(*mIteradorDispositivos).setNumeroDisponiveis( i );
	}
}

void
Nucleo::liberaDispositivo( char vNomeDispositivo )
{
	if( vNomeDispositivo == '0' )
	{
		this->mClock++;
		this->mTempoOcioso++;
		return;
	}
	int
	i;
	this->mIteradorDispositivos = this->mDispositivos.begin();
	while( this->mIteradorDispositivos != this->mDispositivos.end() && (*mIteradorDispositivos).getNome() != vNomeDispositivo )
	{
		this->mClock++;
		this->mTempoOcioso++;
		this->mIteradorDispositivos++;
	}
	if( (*mIteradorDispositivos).getNome() == vNomeDispositivo )
	{
		i = (*mIteradorDispositivos).getNumeroDisponiveis();
		i++;
		(*mIteradorDispositivos).setNumeroDisponiveis( i );
	}
}

void
Nucleo::escalonador()
{
}

void
Nucleo::verificaDispositivos()
{
	int
	i;
	
	char
	c;
	
	this->mIteradorBCP = this->mBloqueadoUsando.begin();
	while( this->mIteradorBCP != this->mBloqueadoUsando.end() )
	{
		BCP
		Auxiliar = *mIteradorBCP;
		Auxiliar.getTarefaAtual( &c, &i );
		if( (*mIteradorBCP).getClockQueSai() <= this->mClock )
		{
			this->liberaDispositivo( c );
			Auxiliar.setUsandoDispositivo( '0' );
			Auxiliar.setTarefaAtual( 0 );
			Auxiliar.proximaTarefa( );
			mIteradorBCP = this->mBloqueadoUsando.erase( mIteradorBCP );
			if( !Auxiliar.fimDoProcesso() )
			{
				this->insereListaPronto( Auxiliar );
			}
		}
		else
		{
			Auxiliar.setTarefaAtual( i - this->mBloqueadoUsando.size() );
		}
		this->mIteradorBCP++;
	}
	
	this->mIteradorBCP = this->mBloqueadoEsperando.begin();
	while( this->mIteradorBCP != this->mBloqueadoEsperando.end() )
	{
		BCP
		Auxiliar = *mIteradorBCP;
		Auxiliar.getTarefaAtual( &c, &i );
		if( this->dispositivoEstaDisponivel( c ) )
		{
			this->insereListaPronto( Auxiliar );
			mIteradorBCP = this->mBloqueadoEsperando.erase( mIteradorBCP );
		}
		mIteradorBCP++;
	}
	this->mClock += 2; //um para verificar a lista usando e um para esperando
	this->mTempoOcioso += 2;
}

void
Nucleo::setTempo( int vTempo )
{
	this->mTempo = vTempo;
}

void
Nucleo::imprimeSituacao()
{
	std::cout << "Clock: " << this->mClock << std::endl << std::endl;
	
	std::cout << "Em Execucao: " << this->mExecucao.getCodigo() << std::endl;
	std::cout << "Tarefa Atual: " << std::endl;
	this->mExecucao.imprimeTarefa();
	std::cout << std::endl << std::endl;
	
	std::cout << "Pronto:";
	mIteradorBCP = mPronto.begin();
	while( mIteradorBCP != mPronto.end() )
	{
		std::cout << " " << (*mIteradorBCP).getCodigo();
		mIteradorBCP++;
	}
	std::cout << std::endl << std::endl;
	
	std::cout << "Bloqueado:";
	mIteradorBCP = mBloqueadoUsando.begin();
	while( mIteradorBCP != mBloqueadoUsando.end() )
	{
		std::cout << " " << (*mIteradorBCP).getCodigo();
		mIteradorBCP++;
	}
	mIteradorBCP = mBloqueadoEsperando.begin();
	while( mIteradorBCP != mBloqueadoEsperando.end() )
	{
		std::cout << " " << (*mIteradorBCP).getCodigo();
		mIteradorBCP++;
	}
	std::cout << std::endl << std::endl;
}

/* ---- Fim dos metodos privados ---- */
